package org.maxgroundstation.groundstation.comm.service;

import java.net.DatagramPacket;
import java.net.SocketException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * A class that handles receiving service UDP communication.
 * @author Scott Davis
 * 
 * Copyright (C) 2010  Scott Davis
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
public class ServiceReceiverUDP implements Runnable {
    
    // Data members
    private Thread receiverThread;
    private ServiceCommunicationUDP comm;
    private Map<String, Long> currentTimestamps;
    private Map<String, ServiceDataFrame> dataFrames;
    private boolean run = false;
    
    /**
     * Constructor
     * @param comm the service UDP communication.
     */
    ServiceReceiverUDP(ServiceCommunicationUDP comm) {
        this.comm = comm;
        
        currentTimestamps = new HashMap<String, Long>();
        dataFrames = new HashMap<String, ServiceDataFrame>();
    }
    
    /**
     * Starts the server thread.
     */
    void start() {
        if ((receiverThread == null) || !run) {
            receiverThread = new Thread(this, "service receiver thread");
            receiverThread.start();
        }
    }
    
    /**
     * Run the server thread.
     */
    public void run() {
    	run = true;
        while (run) {
            if (comm.isConnected()) {
            	try {
            		byte[] buf = new byte[65507];
            		DatagramPacket packet = new DatagramPacket(buf, buf.length);
            		
            		comm.getSocket().receive(packet);
            		
            		byte[] packetData = Arrays.copyOfRange(packet.getData(), 0, packet.getLength());
            		ServiceDataFragment dataFrag = ServiceUtil.parseDataFragment(packetData);
            		comm.getServiceCommunication().setClientID(dataFrag.getHeader().getClientID());
                
            		String serviceID = dataFrag.getHeader().getServiceID();
            		
            		long currentTimestamp = Long.MIN_VALUE;
            		if (currentTimestamps.containsKey(serviceID)) currentTimestamp = currentTimestamps.get(serviceID);
            		long timestamp = dataFrag.getHeader().getTimestamp();
            		//System.out.println(timestamp + " - frag: " + dataFrag.getHeader().getFragNumber());
            		if (timestamp > currentTimestamp) {
            			currentTimestamps.put(serviceID, timestamp);
            			
            			if (dataFrames.containsKey(serviceID)) {
            				ServiceDataFrame dataFrame = dataFrames.get(serviceID);
            				if (!dataFrame.isDataComplete()) System.err.println("Dropping image frame!");
            			}
            			
            			dataFrames.put(serviceID, new ServiceDataFrame(ServiceCommunication.UDP));
            		}
            		else if (timestamp < currentTimestamp) {
            			System.err.println("Old packet timestamp");
            			continue;
            		}
                
            		if (dataFrames.containsKey(serviceID)) {
            			ServiceDataFrame dataFrame = dataFrames.get(serviceID);
            			dataFrame.addDataFragment(dataFrag);
            			
            			if (dataFrame.isDataComplete()) {
            				//System.out.println("Receiving full data frame");
                			//System.out.println(dataFrame.getDataString());
                			comm.getServiceCommunication().handleResponseData(dataFrame);
                			
                			dataFrames.remove(serviceID);
            			}
            		}
            	}
            	catch (SocketException e) {
            		run = false;
            	}
            	catch (Exception e) {
            		System.err.println("Error running UDP service receiver thread: " + e.getMessage());
            	}
            }
        }
    }
}